package cn.xiaopengstack.trigger.http;

import cn.xiaopengstack.domain.activity.model.entity.*;
import cn.xiaopengstack.domain.activity.model.valobj.RechangeTypeVO;
import cn.xiaopengstack.domain.activity.service.IRaffleActivityPartakeService;
import cn.xiaopengstack.domain.activity.service.IRaffleActivityQuotaService;
import cn.xiaopengstack.domain.activity.service.IRaffleActivitySkuProductService;
import cn.xiaopengstack.domain.award.service.IAwardService;
import cn.xiaopengstack.domain.award.model.entity.UserAwardOrderEntity;
import cn.xiaopengstack.domain.award.model.valobj.AwardStateVO;
import cn.xiaopengstack.domain.credit.model.entity.CreditAccountEntity;
import cn.xiaopengstack.domain.credit.model.entity.TradeCreditEntity;
import cn.xiaopengstack.domain.credit.service.ICreditAdjustService;
import cn.xiaopengstack.domain.rebate.model.entity.BehaviorEntity;
import cn.xiaopengstack.domain.rebate.model.entity.BehaviorRebateOrderEntity;
import cn.xiaopengstack.domain.rebate.model.valobj.BehaviorTypeVO;
import cn.xiaopengstack.domain.rebate.service.IBehaviorRebateService;
import cn.xiaopengstack.domain.strategy.model.entity.RaffleAwardEntity;
import cn.xiaopengstack.domain.strategy.model.entity.RaffleFactorEntity;
import cn.xiaopengstack.domain.strategy.model.entity.StrategyAwardEntity;
import cn.xiaopengstack.domain.strategy.model.entity.UserEntity;
import cn.xiaopengstack.domain.strategy.model.valobj.RuleWeightVO;
import cn.xiaopengstack.domain.strategy.service.raffle.IRaffleStrategy;
import cn.xiaopengstack.domain.xxx.model.valobj.TradeNameVO;
import cn.xiaopengstack.domain.xxx.model.valobj.TradeTypeVO;
import cn.xiaopengstack.trigger.api.IRaffleActivityService;
import cn.xiaopengstack.trigger.api.dto.*;
import cn.xiaopengstack.types.annotations.DCCValue;
import cn.xiaopengstack.types.annotations.RateLimiterAccessInterceptor;
import cn.xiaopengstack.types.enums.ResponseCode;
import cn.xiaopengstack.types.exception.AppException;
import cn.xiaopengstack.types.model.Response;
import cn.xiaopengstack.types.utils.TimeUtils;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jiangyangang
 */
@Slf4j
@RestController
@RequestMapping("/api/${app.config.api-version}/raffle/activity")
//@CrossOrigin("${app.config.cross-origin}")
@DubboService(version = "1.0")
public class RaffleActivityController implements IRaffleActivityService {

    @Resource
    private IRaffleActivityPartakeService raffleActivityPartakeService;

    @Resource
    private IRaffleStrategy raffleStrategy;

    @Resource
    private IAwardService awardService;

    @Resource
    private IBehaviorRebateService behaviorRebateService;

    @Resource
    private IRaffleActivityQuotaService raffleActivityQuotaService;

    @Resource
    private IRaffleActivitySkuProductService raffleActivitySkuProductService;

    @Resource
    private ICreditAdjustService creditAdjustService;

    /**
     * 降级开关
     */
    @DCCValue("downgradeSwitch:open")
    private String downgradeSwitch;


    /**
     * 活动抽奖
     * <a href="http://localhost:8091/api/v1/raffle/armory">/api/v1/raffle/activity/raffle_draw</a>
     * @param requestDTO 抽奖请求实体
     * @return
     */
    @SneakyThrows
    @RequestMapping(value = "raffle_draw", method = RequestMethod.POST)
//    @RateLimiterAccessInterceptor(key = "userId", fallbackMethod = "drawRateLimiterError", permitsPerSecond = 1.0d, blacklistCount = 2)
//    @HystrixCommand(commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "150")},
//            fallbackMethod = "drawHystrixError")
    @Override
    public Response<ActivityDrawResponseDTO> drawActivity(@RequestBody ActivityDrawRequestDTO requestDTO) {

//        if ("open".equals(downgradeSwitch)) {
//            return Response.error(null, ResponseCode.DEGRADE_SWITCH);
//        }

//        Thread.sleep(20000000L);

        try {
            // 创建抽奖单
            UserEntity userEntity = getBuild(requestDTO);
            CreatePartakeEntity createPartakeEntity = CreatePartakeEntity.builder()
                    .activityId(requestDTO.getActivityId())
                    .time(new Date())
                    .user(userEntity)
                    .build();
            UserRaffleOrderEntity order = raffleActivityPartakeService.createOrder(createPartakeEntity);

            // 执行抽奖
            RaffleFactorEntity factorEntity = RaffleFactorEntity.builder()
                    .user(UserEntity.builder()
                            .userId(requestDTO.getUserId())
                            .build())
                    .activityEntity(order.getActivityEntity())
                    .build();
            RaffleAwardEntity raffleAwardEntity = raffleStrategy.doRaffle(factorEntity);

            // 保存抽奖记录
            UserAwardOrderEntity awardOrderEntity = UserAwardOrderEntity.builder()
                    .awardEntity(raffleAwardEntity.getAwardEntity())
                    .strategyEntity(raffleAwardEntity.getStrategyEntity())
                    .activityEntity(order.getActivityEntity())
                    .user(userEntity)
                    .orderId(order.getOrderId())
                    .awardState(AwardStateVO.create)
                    .awardTime(raffleAwardEntity.getAwardTime())
                    .user(userEntity)
                    .build();
            awardService.createUserAwardOrder(awardOrderEntity);

            return Response.success(ActivityDrawResponseDTO.builder()
                    .awardIndex(raffleAwardEntity.getAwardEntity().getAwardId())
                    .awardTitle(raffleAwardEntity.getAwardEntity().getAwardTitle())
                    .awardId(raffleAwardEntity.getAwardEntity().getAwardId())
                    .build());
        } catch (AppException app) {
            log.error("用户抽奖，抽奖业务异常；userId={}, activityId={}", requestDTO.getUserId(), requestDTO.getActivityId(), app);
            return Response.error(null, ResponseCode.getResp(app.getCode()).orElse(ResponseCode.UN_ERROR));
        } catch (Exception e) {
            log.error("用户抽奖，抽奖系统异常；userId={}, activityId={}", requestDTO.getUserId(), requestDTO.getActivityId(), e);
            return Response.error(null, ResponseCode.UN_ERROR);
        }
    }

    public Response<ActivityDrawResponseDTO> drawRateLimiterError(ActivityDrawRequestDTO request) {
        log.info("活动抽奖限流 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
        return Response.<ActivityDrawResponseDTO>builder()
                .code(ResponseCode.RATE_LIMITER.getCode())
                .info(ResponseCode.RATE_LIMITER.getInfo())
                .build();
    }

    public Response<ActivityDrawResponseDTO> drawHystrixError(ActivityDrawRequestDTO request) {
        log.info("活动抽奖熔断 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
        return Response.<ActivityDrawResponseDTO>builder()
                .code(ResponseCode.HYSTRIX.getCode())
                .info(ResponseCode.HYSTRIX.getInfo())
                .build();
    }

    private static UserEntity getBuild(ActivityDrawRequestDTO requestDTO) {
        return UserEntity.builder()
                .userId(requestDTO.getUserId())
                .build();
    }

    /**
     * 活动抽奖
     * <a href="http://localhost:8091/api/v1/raffle/armory">/api/v1/raffle/activity/awards</a>
     * @param userId 用户ID
     * @param activityId 活动ID
     * @return
     */
    @RequestMapping(value = "awards", method = RequestMethod.GET)
    @Override
    public Response<List<RaffleAwardListResponseDTO>> queryRaffleStrategyAwardList(@RequestParam String userId,
                                                                         @RequestParam Long activityId) {
        List<StrategyAwardEntity> awardEntities = awardService.queryRaffleStrategyAwardList(userId, activityId);
        List<RaffleAwardListResponseDTO> awards = awardEntities.stream().map(award -> {
            RaffleAwardListResponseDTO responseDTO = new RaffleAwardListResponseDTO();
            BeanUtils.copyProperties(award, responseDTO);
            return responseDTO;
        }).collect(Collectors.toList());

        return Response.success(awards);
    }

    /**
     * 日历签到返利接口
     *
     * @param userId 用户ID
     * @return 签到返利结果
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/calendar_sign_rebate">/api/v1/raffle/activity/calendar_sign_rebate</a>
     * 入参：xiaofuge
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/calendar_sign_rebate -d "userId=xiaofuge" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "calendar_sign_rebate", method = RequestMethod.POST)
    @Override
    public Response<List<String>> calendarSignRebate(@RequestParam String userId,
                                                     @RequestParam Long signTime) {
        log.info("日历签到返利开始 userId:{}", userId);
        BehaviorEntity rechargeEntity = BehaviorEntity.builder()
                .behaviorTime(signTime)
                .behaviorTypeVO(BehaviorTypeVO.SIGN)
                .userEntity(UserEntity.builder().userId(userId).build())
                .outBusinessNo(TimeUtils.getNowTimeStart(TimeUtils.YYYYMMDD))
                .build();
        List<String> order = behaviorRebateService.createOrder(rechargeEntity);
        return Response.success(order);
    }

    /**
     * 查询用户账户额度
     *
     * @param requestDTO
     * @return 账户额度
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/query_user_activity_account">/api/v1/raffle/activity/user_activity_account_quota</a>
     * 入参：requestDTO
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/user_activity_account_quota -d "userId=xiaofuge" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "query_user_activity_account", method = RequestMethod.POST)
    @Override
    public Response<UserActivityAccountResponseDTO> queryUserActivityAccount(UserActivityAccountRequestDTO requestDTO) {
        RaffleActivityAccountEntity userActivityAccountEntity = raffleActivityQuotaService.queryActivityAccountPartakeAccount(requestDTO.getActivityId(), requestDTO.getUserId());
        UserActivityAccountResponseDTO responseDTO = new UserActivityAccountResponseDTO();
        responseDTO.setMonthCount(userActivityAccountEntity.getMonthCount());
        responseDTO.setMonthCountSurplus(userActivityAccountEntity.getMonthCountSurplus());
        responseDTO.setDayCount(userActivityAccountEntity.getDayCount());
        responseDTO.setDayCountSurplus(userActivityAccountEntity.getDayCountSurplus());
        responseDTO.setTotalCount(userActivityAccountEntity.getTotalCount());
        responseDTO.setTotalCountSurplus(userActivityAccountEntity.getTotalCountSurplus());
        return Response.success(responseDTO);
    }

    /**
     * 查询用户是否签到
     *
     * @param userId
     * @return 是否签到
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/is_calendar_sign_rebate">/api/v1/raffle/activity/is_calendar_sign_rebate</a>
     * 入参：jiangyangang
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/is_calendar_sign_rebate -d "userId=jiangyangang" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "is_calendar_sign_rebate", method = RequestMethod.POST)
    @Override
    public Response<Boolean> isCalendarSignRebate(@RequestParam String userId) {
        String outBusinessNo = TimeUtils.getNowTimeStart(TimeUtils.YYYYMMDD);
        List<BehaviorRebateOrderEntity> orderEntities = behaviorRebateService.queryOrderByOutBusinessNo(userId, outBusinessNo);
        return Response.success(CollectionUtils.isNotEmpty(orderEntities));
    }


    /**
     * 查询用户奖品权重信息
     *
     * @param requestDTO
     * @return 奖品权重
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/query_raffle_strategy_rule_weight">/api/v1/raffle/activity/query_raffle_strategy_rule_weight</a>
     * 入参：requestDTO
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/query_raffle_strategy_rule_weight -d "requestDTO" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "query_raffle_strategy_rule_weight", method = RequestMethod.POST)
    @Override
    public Response<RaffleStrategyRuleWeightResponseDTO> queryRaffleStrategyRuleWeight(RaffleStrategyRuleWeightRequestDTO requestDTO) {

        Integer userTotalCount = raffleActivityQuotaService.queryActivityAccountPartakeCount(requestDTO.getActivityId(), requestDTO.getUserId());

        List<RuleWeightVO> ruleWeights = raffleStrategy.queryAwardRuleWeightByActivityId(requestDTO.getActivityId());

        // 组合参数
        List<RaffleStrategyRuleWeightResponseDTO.StrategyWeight> strategyWeights = ruleWeights.stream().map(ruleWeight -> {
            RaffleStrategyRuleWeightResponseDTO.StrategyWeight strategyWeight = new RaffleStrategyRuleWeightResponseDTO.StrategyWeight();
            strategyWeight.setAwardWight(ruleWeight.getAwardWeight());

            List<RaffleStrategyRuleWeightResponseDTO.StrategyAward> awards = ruleWeight.getAwardEntities().stream().map(award -> {
                RaffleStrategyRuleWeightResponseDTO.StrategyAward strategyAward = new RaffleStrategyRuleWeightResponseDTO.StrategyAward();
                strategyAward.setAwardTitle(award.getAwardTitle());
                strategyAward.setAwardId(award.getAwardId());
                return strategyAward;
            }).collect(Collectors.toList());

            strategyWeight.setAwards(awards);

            return strategyWeight;
        }).collect(Collectors.toList());


        RaffleStrategyRuleWeightResponseDTO response = new RaffleStrategyRuleWeightResponseDTO();
        response.setUserTotalAwardsProgress(userTotalCount);
        response.setAwardsWeights(strategyWeights);
        response.setCurrentAward(getCurentStrategyWeight(strategyWeights, userTotalCount));

        return Response.success(response);
    }

    /**
     * 选出当前用户抽奖次数下应该对应哪个权重奖励
     * @param strategyWeights
     * @param userTotalCount
     * @return
     */
    private static RaffleStrategyRuleWeightResponseDTO.StrategyWeight getCurentStrategyWeight(List<RaffleStrategyRuleWeightResponseDTO.StrategyWeight> strategyWeights, Integer userTotalCount) {
        // 根据权重排序
        List<RaffleStrategyRuleWeightResponseDTO.StrategyWeight> sortedStrategyWeights = strategyWeights.stream()
                .sorted(Comparator.comparing(RaffleStrategyRuleWeightResponseDTO.StrategyWeight::getAwardWight).reversed())
                .collect(Collectors.toList());

        // 选出当前用户积分下应该对应哪个权重奖励
        RaffleStrategyRuleWeightResponseDTO.StrategyWeight currentStrategyWeight = new RaffleStrategyRuleWeightResponseDTO.StrategyWeight();
        for (RaffleStrategyRuleWeightResponseDTO.StrategyWeight strategyWeight : sortedStrategyWeights) {
            if (userTotalCount <= strategyWeight.getAwardWight()) {
                currentStrategyWeight = strategyWeight;
                break;
            }
        }

        if (Objects.isNull(currentStrategyWeight.getAwardWight())) {
            currentStrategyWeight = sortedStrategyWeights.get(0);
        }

        return currentStrategyWeight;
    }

    /**
     * 积分兑换
     *
     * @param request
     * @return 兑换结果
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/credit_pay_exchange_sku">/api/v1/raffle/activity/credit_pay_exchange_sku</a>
     * 入参：requestDTO
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/credit_pay_exchange_sku -d "requestDTO" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "credit_pay_exchange_sku", method = RequestMethod.POST)
    @Override
    public Response<Boolean> creditPayExchangeSku(@RequestBody SkuProductShopCartRequestDTO request) {

        log.info("积分兑换商品开始 userId:{} sku:{}", request.getUserId(), request.getSku());

        // 创建一笔充值订单
        ActivitySkuRechargeEntity rechargeEntity = ActivitySkuRechargeEntity.builder()
                .sku(request.getSku())
                .user(UserEntity.builder().userId(request.getUserId()).build())
                .rechargeTime(System.currentTimeMillis())
                .rechangeTypeVO(RechangeTypeVO.CREDIT_EXCHANGE_RECHANGE)
                .outBusinessNo(RandomStringUtils.randomNumeric(12)).build();
        SkuRechargeUnpaidOrderEntity rechargeUnpaidOrder = raffleActivityQuotaService.createOrder(rechargeEntity);
        log.info("积分兑换商品，创建订单完成 userId:{} sku:{} outBusinessNo:{}", request.getUserId(), request.getSku(), rechargeUnpaidOrder.getOutBusinessNo());

        // 支付(调额)积分充值订单
        TradeCreditEntity tradeCreditEntity = TradeCreditEntity.builder()
                .tradeType(TradeTypeVO.REVERSE)
                .userId(request.getUserId())
                // 活动和积分系统如果是分开的话，活动系统的orderId对于积分系统来说就是外部单号outBusinessNo
                .outBusinessNo(rechargeUnpaidOrder.getOrderId())
                .amount(rechargeUnpaidOrder.getPayAmount())
                .tradeName(TradeNameVO.CONVERT_SKU)
                .build();
        String tradeOrderId = creditAdjustService.createOrder(tradeCreditEntity);
        log.info("积分兑换商品，支付订单完成  userId:{} sku:{} orderId:{}", request.getUserId(), request.getSku(), tradeOrderId);

        return Response.success(true);
    }

    /**
     * 查询活动SKU
     *
     * @param activity
     * @return SKU信息
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/query_sku_product_list">/api/v1/raffle/activity/query_sku_product_list</a>
     * 入参：requestDTO
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/query_sku_product_list -d "requestDTO" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "query_sku_product_list", method = RequestMethod.POST)
    @Override
    public Response<List<SkuProductResponseDTO>> querySkuProductListByActivityId(@RequestParam Long activity) {
        List<SkuProductEntity> skuProductEntities = raffleActivitySkuProductService.querySkuProducts(activity);
        if (CollectionUtils.isEmpty(skuProductEntities)) {
            return Response.success(Collections.EMPTY_LIST);
        }

        List<SkuProductResponseDTO> responseDTOS = skuProductEntities.stream().map((entity -> {
            SkuProductResponseDTO dto = new SkuProductResponseDTO();
            BeanUtils.copyProperties(entity, dto);
            dto.setActivityCount(new SkuProductResponseDTO.ActivityCount());

            BeanUtils.copyProperties(entity.getActivityCount(), dto.getActivityCount());
            return dto;
        })).collect(Collectors.toList());

        return Response.success(responseDTOS);
    }

    /**
     * 查询活动SKU
     *
     * @param userId
     * @return 兑换结果
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/query_user_credit_account">/api/v1/raffle/activity/query_user_credit_account</a>
     * 入参：requestDTO
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/query_user_credit_account -d "requestDTO" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "query_user_credit_account", method = RequestMethod.POST)
    @Override
    public Response<BigDecimal> queryUserCreditAccount(@RequestParam String userId) {

        CreditAccountEntity creditAccountEntity = creditAdjustService.queryUserCreditAccount(userId);
        return Response.success(creditAccountEntity.getAdjustAmount());
    }
}


